package com.ynunicom.dc.dingdingcontractapp.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.ynunicom.dc.dingdingcontractapp.dto.exception.FileNotFoundException;
import com.ynunicom.dc.dingdingcontractapp.dto.exception.*;
import com.ynunicom.dc.dingdingcontractapp.entity.ContractEntity;
import com.ynunicom.dc.dingdingcontractapp.entity.RepositoryEntity;
import com.ynunicom.dc.dingdingcontractapp.entity.requestbodyentity.*;
import com.ynunicom.dc.dingdingcontractapp.service.FlowableService;
import com.ynunicom.dc.dingdingcontractapp.service.UserOptionService;
import com.ynunicom.dc.dingdingcontractapp.utils.TaskCrossList;
import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * @author: jinye.Bai
 * @date: 2020/6/2 14:03
 */
@Service
@Slf4j
public class FlowableServiceImpl implements FlowableService {

    @Resource
    RepositoryEntity repositoryEntity;

    @Autowired
    RuntimeService runtimeService;

    @Autowired
    TaskService taskService;

    @Resource
    RestTemplate restTemplate;

    @Resource
    HistoryService historyService;

    @Resource
    UserOptionService userOptionService;

    @Getter
    @Setter
    @Value("${contract.groupName}")
    private String groupName;

    @Getter
    @Setter
    @Value("${contract.filePath}")
    private String filePath;

    @Getter
    @Setter
    @Value("${contract.financialDeptId}")
    private String financialDeptId;

    @Getter
    @Setter
    @Value("${contract.legalDeptId}")
    private String legalDeptId;

    @Getter
    @Setter
    @Value("${contract.leaderDeptId}")
    private String leaderDeptId;

    @Getter
    @Setter
    @Value("${contract.contractSaverDept}")
    private String contractSaverDept;

    /**
     * 查找用户所有角色
     * @param userId
     * @param access_token
     * @return
     */
    @SneakyThrows
    public List<String> roleListCheck(String userId, String access_token){
        JSONObject jsonObject = userOptionService.getUserInfo(new TransBody<>(userId,access_token));
        if (jsonObject.getInteger("errcode")!=0){
            throw new TokenNullException("token错误");
        }
        JSONArray jsonArray = jsonObject.getJSONArray("roles");
        List<String> roleList = new ArrayList<>();
        for (Object o : jsonArray) {
            JSONObject roleObject = (JSONObject) o;
            if (!roleObject.getString("groupName").equals(groupName)) {
                continue;
            }
            roleList.add(roleObject.getString("name"));
        }
        return roleList;
    }


    /**
     * 计算出基于baseDeptId的最高部门Id，不包括根部门
     * @param jsonObject 查询上级部门列表接口的返回值
     * @param baseDeptId 要查询的ID
     * @return
     */
    public String getSuperDeptId(JSONObject jsonObject,String baseDeptId,String processInstanceId) throws RsponseEmptyException {
        if (jsonObject==null||jsonObject.isEmpty()){
            throw new RsponseEmptyException("查询上级部门失败",processInstanceId);
        }
        if (!"0".equals(jsonObject.getString("errcode"))){
            throw new RsponseEmptyException("钉钉服务器返回失败,可能是token失效,或者访问ip不在钉钉白名单",processInstanceId);
        }

        String superDeptId = null;
        JSONArray allArray = jsonObject.getJSONArray("department");
        if (allArray.size()<=0){
            throw new RsponseEmptyException("此人尚未加入任何部门",processInstanceId);
        }
        for (int count=0;count<allArray.size();count++){
            JSONArray innerJsonArray= allArray.getJSONArray(count);

            /**
             * 此处必须使用Integer,因为被拿来对比的就是Integer
             */
            if (innerJsonArray.size()>0&&innerJsonArray.contains(Integer.parseInt(baseDeptId))){
                superDeptId = innerJsonArray.getString(innerJsonArray.size()-2);
            }
            if (innerJsonArray.size()==1){
                return "1";
            }
        }
        return superDeptId;

    }


    /**
     * 给出所有与传入用户有关的任务
     * @param transBody
     * @return
     */
    @Override
    public JSONObject getAllAssignedTasks(TransBody<FlowableTaskCheckRequestBody> transBody) {

        List<String> departmentList = transBody.getRequestBody().getDepartment();
        List<String> roleList = roleListCheck(transBody.getRequestBody().getUserId(),transBody.getAccess_token());
        List<Task> taskList = new ArrayList<>();
        for (String department:departmentList
             ) {
            for (String role:roleList
                 ) {
                /**
                 * 取交集
                 */
                taskList.addAll(TaskCrossList.taskCrossList(taskService.createTaskQuery().taskCandidateGroup(department).list(),taskService.createTaskQuery().taskCandidateUser(role).list()));
            }
        }
        taskList.addAll(taskService.createTaskQuery().taskAssignee(transBody.getRequestBody().getUserId()).list());
        JSONObject jsonObject = new JSONObject();
        int counter = 0;
        for (Task task:taskList){
            JSONObject innerJson = new JSONObject();
            innerJson.put("taskId",task.getId());
            innerJson.put("data",taskService.getVariables(task.getId()));
            innerJson.put("stage",task.getName());
            jsonObject.put("task"+ counter,innerJson);
            counter++;
        }
        return jsonObject;
    }


    /**
     * 传入合同参数，开始一个新的实例
     * @param processStartRequestBody
     * @param file
     * @param access_token
     * @return
     */

    @SneakyThrows
    @Override
    public JSONObject processStart(ProcessStartRequestBody processStartRequestBody, MultipartFile file, String access_token) {
        File downloadFile = new File(filePath+'/'+Objects.requireNonNull(file.getOriginalFilename()));
        File upperPath = new File(filePath);
        if (!upperPath.exists()){
            if (!upperPath.mkdir()){
                throw new FailToMakeDir(String.format("创建文件夹[%s]失败",upperPath.getAbsolutePath()));
            }
            if(!downloadFile.createNewFile()){
                throw new FailToMakeDir(String.format("创建文件[%s]失败",downloadFile.getAbsolutePath()));
            }
        }
        InputStream inputStream = file.getInputStream();
        byte[] buffer = new byte[inputStream.available()];
        inputStream.read(buffer);
        FileOutputStream fileOutputStream = new FileOutputStream(downloadFile);
        fileOutputStream.write(buffer);
        fileOutputStream.flush();
        fileOutputStream.close();
        inputStream.close();

        processStartRequestBody.getContractEntity().setPath(file.getOriginalFilename());
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(repositoryEntity.getKey());
        Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
        Map<String,Object> map = new HashMap<>();
        String superDeptId = getSuperDeptId(userOptionService.allUpperDeptPath(new TransBody<>(processStartRequestBody.getUserId(),access_token)),processStartRequestBody.getDeptId(),task.getProcessInstanceId());
        map.put("applyUserId", processStartRequestBody.getUserId());
        map.put("deptId",processStartRequestBody.getDeptId());
        map.put("superDeptId",superDeptId);
        map.put("contract",processStartRequestBody.getContractEntity());
        map.put("financialDeptId",financialDeptId);
        map.put("legalDeptId",legalDeptId);
        map.put("leaderDeptId",leaderDeptId);
        map.put("contractSaverDept",contractSaverDept);


        /**
         * 置入各级审批,前端检查以下审批的值，如果都为true，则合同通过审批，正在运行
         * 如果有任何一个为false，则合同需要更改，在更改环节
         */
        map = refreashMap(map);

        taskService.addComment(task.getId(),task.getProcessInstanceId(),"applyUserId", processStartRequestBody.getUserId());
        taskService.addComment(task.getId(),task.getProcessInstanceId(),"contractName", processStartRequestBody.getContractEntity().getName());
        taskService.addComment(task.getId(),task.getProcessInstanceId(),"contractDisc", processStartRequestBody.getContractEntity().getDisc());


        taskService.complete(task.getId(),map);

        JSONObject returner = new JSONObject();
        returner.put("startVar",map);
        return returner;
    }


    /**
     * 传入文件名，返回文件
     * @param httpServletResponse
     * @param fileName
     */
    @SneakyThrows
    @Override
    public void getFile(HttpServletResponse httpServletResponse, String fileName) {
        if ("".equals(fileName)){
            throw new ParamNullException("文件名不能为空");
        }
        File file = new File(filePath+'/'+fileName);
        if (!file.exists()){
            throw new FileNotFoundException(fileName);
        }
        httpServletResponse.setHeader("Content-Disposition", "attachment;fileName="+ Arrays.toString(fileName.getBytes(StandardCharsets.UTF_8)));
        httpServletResponse.setContentType("application/force-download");
        FileInputStream fileInputStream = new FileInputStream(file);
        BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
        OutputStream outputStream = httpServletResponse.getOutputStream();
        byte[] buffer = new byte[fileInputStream.available()];
        bufferedInputStream.read(buffer);
        outputStream.write(buffer);
        outputStream.flush();
        outputStream.close();
        bufferedInputStream.close();
        fileInputStream.close();
    }


    /**
     * 部门审核
     * @param transBody
     * @return
     */
    @Override
    public JSONObject deptJudgeAndComment(TransBody<DeptJudgeAndComment> transBody) {

        Map<String,Object> map = taskService.getVariables(transBody.getRequestBody().getTaskId());
        Task task = taskService.createTaskQuery().taskId(transBody.getRequestBody().getTaskId()).singleResult();
        if (Boolean.parseBoolean(transBody.getRequestBody().getIsApprove())){
            map.put("isDeptOk","true");
        }
        else {
            map.put("isDeptOk","false");
        }
        map.put("deptComment",transBody.getRequestBody().getComment());

        taskService.addComment(task.getId(),task.getProcessInstanceId(),"deptJudgeUserId",transBody.getRequestBody().getUserId());
        taskService.addComment(task.getId(),task.getProcessInstanceId(),"deptJudge",transBody.getRequestBody().getIsApprove());
        taskService.addComment(task.getId(),task.getProcessInstanceId(),"deptComment",transBody.getRequestBody().getComment());

        taskService.complete(transBody.getRequestBody().getTaskId(),map);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("taskId",transBody.getRequestBody().getTaskId());
        jsonObject.put("variable",map);
        return jsonObject;
    }


    /**
     * 部门经理审核
     * @param transBody
     * @return
     */
    @Override
    public JSONObject managerJudgeAndComment(TransBody<ManagerJudgeAndCommentRequestBody> transBody) {

        Map<String,Object> map = taskService.getVariables(transBody.getRequestBody().getTaskId());
        Task task = taskService.createTaskQuery().taskId(transBody.getRequestBody().getTaskId()).singleResult();
        if (Boolean.parseBoolean(transBody.getRequestBody().getIsApprove())){
            map.put("isManagerOk","true");
        }
        else {
            map.put("isManagerOk","false");
        }
        map.put("managerComment",transBody.getRequestBody().getComment());

        taskService.addComment(task.getId(),task.getProcessInstanceId(),"managerJudgeUserId",transBody.getRequestBody().getUserId());
        taskService.addComment(task.getId(),task.getProcessInstanceId(),"managerJudge",transBody.getRequestBody().getIsApprove());
        taskService.addComment(task.getId(),task.getProcessInstanceId(),"managerComment",transBody.getRequestBody().getComment());

        taskService.complete(transBody.getRequestBody().getTaskId(),map);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("taskId",transBody.getRequestBody().getTaskId());
        jsonObject.put("variable",map);
        return jsonObject;
    }

    @Override
    public JSONObject financialJudgeAndComment(TransBody<FinancialJudgeAndCommentRequestBody> transBody) {
        Map<String,Object> map = taskService.getVariables(transBody.getRequestBody().getTaskId());
        Task task = taskService.createTaskQuery().taskId(transBody.getRequestBody().getTaskId()).singleResult();
        if (Boolean.parseBoolean(transBody.getRequestBody().getIsApprove())){
            map.put("isFinancialOk","true");
        }
        else {
            map.put("isFinancialOk","false");
        }
        map.put("financialComment",transBody.getRequestBody().getComment());

        taskService.addComment(task.getId(),task.getProcessInstanceId(),"financialJudgeUserId",transBody.getRequestBody().getUserId());
        taskService.addComment(task.getId(),task.getProcessInstanceId(),"financialJudge",transBody.getRequestBody().getIsApprove());
        taskService.addComment(task.getId(),task.getProcessInstanceId(),"financialComment",transBody.getRequestBody().getComment());

        taskService.complete(transBody.getRequestBody().getTaskId(),map);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("taskId",transBody.getRequestBody().getTaskId());
        jsonObject.put("variable",map);
        return jsonObject;
    }

    @Override
    public JSONObject legalJudgeAndComment(TransBody<LegalJudgeAndCommentRequestBody> transBody) {
        Map<String,Object> map = taskService.getVariables(transBody.getRequestBody().getTaskId());
        Task task = taskService.createTaskQuery().taskId(transBody.getRequestBody().getTaskId()).singleResult();
        if (Boolean.parseBoolean(transBody.getRequestBody().getIsApprove())){
            map.put("isLegalOk","true");
        }
        else {
            map.put("isLegalOk","false");
        }
        map.put("legalComment",transBody.getRequestBody().getComment());

        taskService.addComment(task.getId(),task.getProcessInstanceId(),"legalJudgeUserId",transBody.getRequestBody().getUserId());
        taskService.addComment(task.getId(),task.getProcessInstanceId(),"legalJudge",transBody.getRequestBody().getIsApprove());
        taskService.addComment(task.getId(),task.getProcessInstanceId(),"legalComment",transBody.getRequestBody().getComment());

        taskService.complete(transBody.getRequestBody().getTaskId(),map);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("taskId",transBody.getRequestBody().getTaskId());
        jsonObject.put("variable",map);
        return jsonObject;
    }

    @Override
    public JSONObject leaderJudgeAndComment(TransBody<LeaderJudgeAndCommentRequestBody> transBody) {
        Map<String,Object> map = taskService.getVariables(transBody.getRequestBody().getTaskId());
        Task task = taskService.createTaskQuery().taskId(transBody.getRequestBody().getTaskId()).singleResult();
        if (Boolean.parseBoolean(transBody.getRequestBody().getIsApprove())){
            map.put("isLeaderOk","true");
        }
        else {
            map.put("isLeaderOk","false");
        }
        map.put("leaderComment",transBody.getRequestBody().getComment());

        taskService.addComment(task.getId(),task.getProcessInstanceId(),"leaderJudgeUserId",transBody.getRequestBody().getUserId());
        taskService.addComment(task.getId(),task.getProcessInstanceId(),"leaderJudge",transBody.getRequestBody().getIsApprove());
        taskService.addComment(task.getId(),task.getProcessInstanceId(),"leaderComment",transBody.getRequestBody().getComment());

        taskService.complete(transBody.getRequestBody().getTaskId(),map);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("taskId",transBody.getRequestBody().getTaskId());
        jsonObject.put("variable",map);
        return jsonObject;
    }

    @Override
    public JSONObject contractSaverJudgeAndComment(TransBody<SaverJudgeAndComment> transBody) {
        Map<String,Object> map = taskService.getVariables(transBody.getRequestBody().getTaskId());
        Task task = taskService.createTaskQuery().taskId(transBody.getRequestBody().getTaskId()).singleResult();
        if (Boolean.parseBoolean(transBody.getRequestBody().getIsApprove())){
            map.put("isContractSaverOk","true");
        }
        else {
            map.put("isContractSaverOk","false");
        }
        map.put("contractSaverComment",transBody.getRequestBody().getComment());

        taskService.addComment(task.getId(),task.getProcessInstanceId(),"contractSaverJudgeUserId",transBody.getRequestBody().getUserId());
        taskService.addComment(task.getId(),task.getProcessInstanceId(),"contractSaverJudge",transBody.getRequestBody().getIsApprove());
        taskService.addComment(task.getId(),task.getProcessInstanceId(),"contractSaverComment",transBody.getRequestBody().getComment());

        taskService.complete(transBody.getRequestBody().getTaskId(),map);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("taskId",transBody.getRequestBody().getTaskId());
        jsonObject.put("variable",map);
        return jsonObject;
    }

    /**
     * 发起人更正
     * @param recorrectRequestBody
     * @param file
     * @param access_token
     * @return
     */
    @SneakyThrows
    @Override
    public JSONObject recorrect(RecorrectRequestBody recorrectRequestBody,MultipartFile file,String access_token) {

        String taskId = recorrectRequestBody.getTaskId();
        Map<String,Object> map = taskService.getVariables(taskId);
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (!file.isEmpty()){
            ContractEntity contractEntity = (ContractEntity) map.get("contract");
            String oldFilePath = contractEntity.getPath();
            File oldFile = new File(filePath+'/'+oldFilePath);
            if (oldFile.exists()){
                oldFile.delete();
            }
            File newFile = new File(filePath+'/'+file.getOriginalFilename());
            File upperFilePath = new File(filePath);
            if (!upperFilePath.exists()){
                if (!upperFilePath.mkdir()){
                    throw new FailToMakeDir("创建上级目录失败");
                }
            }
            if (newFile.exists()){
                throw new FileNotFoundException("文件已经存在");
            }
            if (!newFile.createNewFile()){
                throw new FailToMakeDir("创建文件失败");
            }
            FileOutputStream fileOutputStream = new  FileOutputStream(newFile);
            InputStream inputStream = file.getInputStream();
            BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
            byte[] buffer = new byte[inputStream.available()];
            bufferedInputStream.read(buffer);
            fileOutputStream.write(buffer);
            fileOutputStream.flush();
            bufferedInputStream.close();
            inputStream.close();
            fileOutputStream.close();
            recorrectRequestBody.getContractEntity().setPath(file.getOriginalFilename());
        }
        if (file.isEmpty()){
            recorrectRequestBody.getContractEntity().setPath(((ContractEntity)map.get("contract")).getPath());
        }
        map.put("contract",recorrectRequestBody.getContractEntity());
        map.put("deptId",recorrectRequestBody.getDeptId());

        taskService.addComment(taskId,task.getProcessInstanceId(),"recorrectApplyer",recorrectRequestBody.getUserId());

        taskService.complete(taskId,map);
        JSONObject returner = new JSONObject();
        returner.put("startVar",map);
        return returner;
    }

    @Override
    public JSONObject runningContract(TransBody<String> transBody) {

        List<Task> taskListForAssign = taskService.createTaskQuery().taskAssignee(transBody.getRequestBody()).list();
        List<Task> taskListForKey = taskService.createTaskQuery().taskDefinitionKey("runningContract").list();
        List<Task> taskList = TaskCrossList.taskCrossList(taskListForAssign,taskListForKey);
        JSONObject jsonObject = new JSONObject();
        int count = 0;
        for (Task task :
                taskList) {
            JSONObject innerJSONObject = new JSONObject();
            innerJSONObject.put("data",taskService.getVariables(task.getId()));
            innerJSONObject.put("taskId",task.getId());
            innerJSONObject.put("stage",task.getName());
            jsonObject.put("task"+count,innerJSONObject);
        }

        return jsonObject;

    }

    @Override
    public JSONObject alterRunningContract(TransBody<AlterRequestBody> transBody) {
        Map<String,Object> map = taskService.getVariables(transBody.getRequestBody().getTaskId());
        Task task = taskService.createTaskQuery().taskId(transBody.getRequestBody().getTaskId()).singleResult();
        JSONObject jsonObject = new JSONObject();
        if (transBody.getRequestBody().getEndMethod().equals("alter")){
            map.put("endMethod",transBody.getRequestBody().getEndMethod());
            map.put("alterComment",transBody.getRequestBody().getComment());
            map = refreashMap(map);
            jsonObject.put("endMethod",transBody.getRequestBody().getEndMethod());

            taskService.addComment(task.getId(),task.getProcessInstanceId(),"alterApplyer",transBody.getRequestBody().getUserId());
            taskService.addComment(task.getId(),task.getProcessInstanceId(),"alterComment",transBody.getRequestBody().getComment());
            taskService.addComment(task.getId(),task.getProcessInstanceId(),"endMethod",transBody.getRequestBody().getEndMethod());

            taskService.complete(transBody.getRequestBody().getTaskId(),map);
        }
        else if (transBody.getRequestBody().getEndMethod().equals("endNotByTime")){
            map.put("endMethod",transBody.getRequestBody().getEndMethod());
            map.put("alterComment",transBody.getRequestBody().getComment());
            map = refreashMap(map);
            jsonObject.put("endMethod",transBody.getRequestBody().getEndMethod());

            taskService.addComment(task.getId(),task.getProcessInstanceId(),"alterApplyer",transBody.getRequestBody().getUserId());
            taskService.addComment(task.getId(),task.getProcessInstanceId(),"alterComment",transBody.getRequestBody().getComment());
            taskService.addComment(task.getId(),task.getProcessInstanceId(),"endMethod",transBody.getRequestBody().getEndMethod());

            taskService.complete(transBody.getRequestBody().getTaskId(),map);
        }
        jsonObject.put("taskId",transBody.getRequestBody().getTaskId());
        return jsonObject;
    }

    public Map<String,Object> refreashMap(Map<String,Object> map){
        map.put("isDeptOk","false");
        map.put("isManagerOk","false");
        map.put("isFinancialOk","false");
        map.put("isLegalOk","false");
        map.put("isLeaderOk","false");
        map.put("isContractSaverOk","false");
        return map;
    }
}
